package com.cy.pj.sys.service.impl;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import com.cy.pj.common.annotation.RequiredLog;
import com.cy.pj.common.bo.PageObject;
import com.cy.pj.common.bo.SysUserDept;
import com.cy.pj.common.exception.ServiceException;
import com.cy.pj.common.util.ShiroUtils;
import com.cy.pj.sys.dao.SysUserDao;
import com.cy.pj.sys.dao.SysUserRoleDao;
import com.cy.pj.sys.pojo.SysUser;
import com.cy.pj.sys.service.SysUserService;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.util.StringUtil;

/**
 * @Transectional 属性信息:
 * timeout - 超时时间(秒) 默认-1 一直等
 * rollbackFor - 回滚的异常类型 (异常类型的Class) // 默认RunTimeException的异常都回滚
 * noRollbackFor - 不回滚的异常类型 (异常类型的Class)
 * readonly - 只读事务(查询应为只读事务) 在只读事务中不允出现更新等写操作 false允许 查询操作事务属性readonly查询性能高
 * isolation - 隔离级别 ,多个事务并发执行时可能会出现什么问题?脏读,幻读,不可重复读
 * 
 * propagation - 事务传播特性
 * Propagation.REQUIRED -> 没有事务新创建事务,有事务则参与到当前事务
 * Propagation.REQUIRES_NEW -> 不论有没有事务都会创建新的事务进行处理当前操作
 * 使用:必须是两个服务中的不同事务,如果需要让每个事务隔离运行则使用new,否则使用上边那个
 * 例如用户查询和写日志就需要使用new,详见日志的saveLog方法和本类的findPageObject是方法
 */
@Transactional(timeout = 30,rollbackFor = Throwable.class,noRollbackFor = ServiceException.class,
	readOnly = false,isolation = Isolation.READ_COMMITTED)
@Service
public class SysUserServiceImpl implements SysUserService {
	@Autowired
	private SysUserDao dao;
	
	@Autowired
	private SysUserRoleDao sysUserRoleDao;
	
	/**
	 * 修改登录密码
	 */
	public int updatePaword(String sourcePassword,String newPassword,String cfgPasssword) {
		//参数校验
		if(StringUtils.isEmpty(sourcePassword)||StringUtils.isEmpty(newPassword)||StringUtils.isEmpty(cfgPasssword))
			throw new IllegalArgumentException("输入不允许为空!");
		if(!newPassword.equals(cfgPasssword))
			throw new IllegalArgumentException("两次新密码输入不一致!");
		//获取登录的用户
		SysUser user = ShiroUtils.getUser();
		//校验原密码
		SimpleHash sh = new SimpleHash("MD5", sourcePassword, user.getSalt(), 1);
		String hex = sh.toHex();
		String pwd = user.getPassword();
		if(pwd==null||!pwd.equals(hex)) 
			throw new IllegalArgumentException("原密码输入有误!");
		//修改密码
		//获取盐值
		String salt = UUID.randomUUID().toString();
		//计算新的加密密码
		sh = new SimpleHash("MD5",newPassword,salt,1);
		String saltPwd = sh.toHex();
		int rows = dao.updatePaword(user.getId(),user.getUsername(), saltPwd, salt);
		//回传结果
		if(rows==0) throw new ServiceException("保存密码失败!");
		return rows;
	}
	
	/**
	 * 这里想加只读事务必须设置切面中日志操作的优先级,否则会先执行本方法,而日志切面会做新增,这就会出现异常
	 */
	@Transactional(readOnly = true)
	@RequiredLog(key = "查询用户操作")
	@Override
	public PageObject<SysUserDept> findPageObject(String username, Integer pageCurrent) {
		//1.参数校验
		//2.查询总记录数并校验
//		int rowCount = dao.getRowCount(username);
//		if(rowCount==0) {
//			throw new IllegalArgumentException("没查到用户!");
//		}
		//3.查询当前页记录
		int pageSize = 3;
		//4.封装查询结果并返回
		/*
		 *  SELECT count(0) FROM sys_users u LEFT JOIN sys_depts d ON u.deptId = d.id 
			==> Parameters7
			<==      Total: 1
			==>  Preparing: select u.*,d.name,d.parentId from sys_users u left join sys_depts d on 
					u.deptId=d.id order by u.createdTime desc LIMIT ? 
			==> Parameters: 3(Integer)
		 */
		//使用PageHelper最关键的一个步骤,这里表示启动pageHelper的内置拦截器,拦截查询请求,庵后UdineSQl做处理
		Page<SysUserDept> page = PageHelper.startPage(pageCurrent,pageSize);
		List<SysUserDept> list = dao.findPageObject(username);
//		return new PageObject<>((long)rowCount, (long)pageCurrent, pageSize, list);
//		Long pageCurrent, Integer pageSize, Long rowCount, Long pageCount, List<T> records
		/* page.getPages():总记录条数   page.getTotal() 总记录数 */
		return new PageObject<>((long)pageCurrent,pageSize,page.getTotal(),(long)page.getPages(),list);
	}

	@RequiresPermissions("sys:user:update")
	@RequiredLog(key="处理用户启用禁用")
	@Override
	public int validById(Integer id, Integer valid) {
		int rows = dao.validById(id, valid, "admin");
		if(rows==0) {
			throw new ServiceException("记录可能已经不存在!");
		}
		return rows;
	}
	/**
	 * @Transactional 注解描述的方法为一个事务切入点方法
	 * 此注解描述的方法在执行时:
	 * 1)开始之前会开启事务
	 * 2)方法结束时会提交事务
	 * 3)方法执行过程中如果出现异常会回滚事务
	 * 在业务层对象的类上加此注解则表示整个类的所有方法都需要启用事务控制
	 * DataSourceTransactionManager
	 * FAQ:事务控制代码你写了么?没写
	 * 		如果使我们自己写会写在哪? 切面对象方法中(通知方法),框架中帮我们写了很多切面方法包括事务的通知方法
	 */
	@Transactional
	@RequiredLog(key="处理保存用户")
	@Override
	public int doSaveObject(SysUser sysUser, Integer... roleIds) {
		//1.校验数据
		if(sysUser==null||roleIds==null||roleIds.length<1) {
			throw new IllegalArgumentException("请输入正确的参数信息!");
		}
		//2.进行密码加密
		String source = sysUser.getPassword();
		UUID u = UUID.randomUUID();
		System.out.println("UUID.randomUUID()="+u);
		String salt = u.toString();//随机字符串
		System.out.println("salt="+salt);
		/*
		 * 对密码进行盐值加密(加密算法MD5-特点:不可逆,相同内容加密结果也相同)
		 */
		//方式1(借助Spring框架的工具类DigestUtils)
		//String newPassword = DigestUtils.md5DigestAsHex((salt+sysUser.getPassword()).getBytes());
		//方式2(借助shior的框架)
		SimpleHash sh = new SimpleHash("MD5",source,salt,1);
		sysUser.setSalt(salt);
		String newPassword = sh.toHex();
		sysUser.setPassword(newPassword);
		System.out.println(newPassword);
		//3.先创建用户信息
		int rows = dao.doSaveObject(sysUser);
		//4.再添加用户角色对应信息
		sysUserRoleDao.insertObjects(sysUser.getId(), roleIds);
		return rows;
	}

	@Override
	public Map<String,Object> doFindObjectById(Integer id) {
		//1.参数的校验
		if(id==null || id<1) {
			throw new IllegalArgumentException("请选择要修改的用户");
		}
		//2.查询user表的数据和dept表的部门名形成对照
		SysUserDept sysUserDept = dao.findObjectById(id);
		if(sysUserDept==null){
			throw new ServiceException("用户可能已经不存在!");
		}
		//3.查询指定对应的role角色
		List<Integer> list = sysUserRoleDao.findRolesByUserId(id);
		Map<String,Object> map = new HashMap<>();
		map.put("user", sysUserDept);
		map.put("roleIds", list);
		return map;
	}

	@RequiredLog(key="处理修改用户")
	@Override
	public int doUpdateObject(SysUser sysUser, Integer[] roleIds) {
		//1.进行参数校验
		if(sysUser==null||StringUtil.isEmpty(sysUser.getUsername())||roleIds==null||roleIds.length<1) {
			throw new IllegalArgumentException("请输入正确的修改信息!");
		}
		//2.先进行用户数据的修改
		int rows = dao.updateObjectById(sysUser);
		if(rows==0) {
			throw new ServiceException("此用户可能已经不存在!");
		}
		//3.再删除用户角色对应关系之后重新创建此用户
		sysUserRoleDao.deleteObjectByUserId(sysUser.getId());
		sysUserRoleDao.insertObjects(sysUser.getId(), roleIds);
		return rows;
	}

}
